package com.dtp.core.monitor.collector;

import cn.hutool.core.bean.BeanUtil;
import com.dtp.common.entity.ThreadPoolStats;
import com.dtp.common.em.CollectorTypeEnum;
import com.dtp.common.util.CommonUtil;
import com.google.common.collect.Lists;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Micrometer收集器
 *
 * @author yanhom
 * @since 1.0.0
 *
 * @remark muse
 */
@Slf4j
public class MicroMeterCollector extends AbstractCollector {
    public static final String DTP_METRIC_NAME_PREFIX = "thread.pool";
    public static final String POOL_NAME_TAG = DTP_METRIC_NAME_PREFIX + ".name"; // "thread.pool.name"
    public static final String APP_NAME_TAG = "app.name";
    private static final Map<String, ThreadPoolStats> GAUGE_CACHE = new ConcurrentHashMap<>(); // poolName:ThreadPoolStats

    @Override
    public void collect(ThreadPoolStats threadPoolStats) {
        ThreadPoolStats oldStats = GAUGE_CACHE.get(threadPoolStats.getPoolName());

        // 如果缓存中不存在，则将线程池状态维护到缓存中
        if (Objects.isNull(oldStats)) GAUGE_CACHE.put(threadPoolStats.getPoolName(), threadPoolStats);

        // 如果存在，则以threadPoolStats为基准，去更新旧的线程池状态
        else BeanUtil.copyProperties(threadPoolStats, oldStats);

        gauge(GAUGE_CACHE.get(threadPoolStats.getPoolName()));
    }

    /** 创建Gauge对象并注册到Metrics实例中 */
    public void gauge(ThreadPoolStats poolStats) {
        Iterable<Tag> tags = Lists.newArrayList(
                Tag.of(POOL_NAME_TAG, poolStats.getPoolName()), // "thread.pool.name"
                Tag.of(APP_NAME_TAG, CommonUtil.getInstance().getServiceName())); // "app.name"

        /**
         * 创建一个Gauge对象，并为其指定其名称、度量指标的标签、对象、从对象中获得值的计算函数。
         * 然后，可以将这个Gauge对象注册到Metrics实例中，以便在程序运行期间进行监控和分析。
         */
        Metrics.gauge(metricName("core.size"), tags, poolStats, ThreadPoolStats::getCorePoolSize);
        Metrics.gauge(metricName("maximum.size"), tags, poolStats, ThreadPoolStats::getMaximumPoolSize);
        Metrics.gauge(metricName("current.size"), tags, poolStats, ThreadPoolStats::getPoolSize);
        Metrics.gauge(metricName("largest.size"), tags, poolStats, ThreadPoolStats::getLargestPoolSize);
        Metrics.gauge(metricName("active.count"), tags, poolStats, ThreadPoolStats::getActiveCount);
        Metrics.gauge(metricName("task.count"), tags, poolStats, ThreadPoolStats::getTaskCount);
        Metrics.gauge(metricName("completed.task.count"), tags, poolStats, ThreadPoolStats::getCompletedTaskCount);
        Metrics.gauge(metricName("wait.task.count"), tags, poolStats, ThreadPoolStats::getWaitTaskCount);
        Metrics.gauge(metricName("queue.size"), tags, poolStats, ThreadPoolStats::getQueueSize);
        Metrics.gauge(metricName("queue.capacity"), tags, poolStats, ThreadPoolStats::getQueueCapacity);
        Metrics.gauge(metricName("queue.remaining.capacity"), tags, poolStats, ThreadPoolStats::getQueueRemainingCapacity);
        Metrics.gauge(metricName("reject.count"), tags, poolStats, ThreadPoolStats::getRejectCount);
        Metrics.gauge(metricName("run.timeout.count"), tags, poolStats, ThreadPoolStats::getRunTimeoutCount);
        Metrics.gauge(metricName("queue.timeout.count"), tags, poolStats, ThreadPoolStats::getQueueTimeoutCount);
    }

    /** 指标名称为："thread.pool.[name]" */
    private static String metricName(String name) {return String.join(".", DTP_METRIC_NAME_PREFIX, name);}

    /** 当前收集器类型为"micrometer" */
    @Override
    public String type() {return CollectorTypeEnum.MICROMETER.name().toLowerCase();}
}

